﻿using NClass.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnalyticsEngine.ClonesDetection
{
	public class ClonesDetectionEngine
	{
		private Queue<DiagrammPartInfo> m_searchQueue = new Queue<DiagrammPartInfo>();
		private List<DiagrammPartInfo> m_processed = new List<DiagrammPartInfo>();
		private List<DiagrammPartInfo> m_allCloneCandidates = new List<DiagrammPartInfo>();
		private Dictionary<DiagrammPartInfo, List<DiagrammPartInfo>> m_results = new Dictionary<DiagrammPartInfo, List<DiagrammPartInfo>>();
		private byte m_range = 0;
		private Model m_model = null;

		public ClonesDetectionEngine(Model m)
		{
			DiagramModel = m;
		}

		protected List<DiagrammPartInfo> AllCloneCandidates
		{
			get
			{
				return m_allCloneCandidates;
			}
			set
			{
				m_allCloneCandidates = value;
			}
		}

		protected Queue<DiagrammPartInfo> SearchQueue
		{
			get
			{
				return m_searchQueue;
			}
		}

		public Dictionary<DiagrammPartInfo, List<DiagrammPartInfo>> Results
		{
			get
			{
				return m_results;
			}
		}

		protected Model DiagramModel
		{
			get
			{
				return m_model;
			}
			set
			{
				m_model = value;
			}
		}

		public byte Range
		{
			get
			{
				return m_range;
			}
			protected set
			{
				if (Range == 0)
				{
					m_range = value;
				}
			}
		}

		public List<DiagrammPartInfo> Processed
		{
			get
			{
				return m_processed;
			}
		}

		private void InitCloneCandidates()
		{
			Model m = DiagramModel;
			List<int> counters = new List<int>(Range);
			for (byte i = 0; i < Range; ++i)
			{
				counters.Add(0);
			}
			int maxCounter = m.Entities.ToArray<object>().Length;

			List<IEntity> modelEntities = m.Entities.ToList<IEntity>();
			while (!CountersUp(counters, maxCounter))
			{
				if (CountersListIsUnique(counters))
				{
					List<TypeBase> candidateSet = new List<TypeBase>();
					bool isAvailableSet = true;
					for (byte i = 0; i < Range; ++i)
					{
						IEntity e = modelEntities[counters[i]];
						if (e.EntityType == EntityType.Class || e.EntityType == EntityType.Interface)
						{
							candidateSet.Add(e as TypeBase);
						}
						else
						{
							isAvailableSet = false;
							break;
						}
					}
					if (isAvailableSet)
					{
						DiagrammPartInfo candidate = new DiagrammPartInfo(candidateSet, m);
						if (candidate.IsCoupled)
						{
							bool isNotExamplepart = true;
							foreach (DiagrammPartInfo pi in SearchQueue)
							{
								isNotExamplepart = !candidate.Intersect(pi);
								if (!isNotExamplepart)
								{
									break;
								}
							}
							if (isNotExamplepart)
							{
								AllCloneCandidates.Add(candidate);
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="counters"></param>
		/// <param name="maxCounter"></param>
		/// <returns>True if counting done</returns>
		private bool CountersUp(List<int> counters, int maxCounter)
		{
			bool result = true;
			bool upNext = true;
			for (byte i = 0; i < counters.Count && upNext; ++i)
			{				
				if (upNext)
				{
					++counters[i];
				}
				upNext = counters[i] == maxCounter;
				if (upNext)
				{
					counters[i] = 0;
				}
			}
			for (byte i = 0; i < counters.Count && result; ++i)
			{
				result &= counters[i] == maxCounter - 1;
			}
			return result;
		}

		private bool CountersListIsUnique(List<int> counters)
		{
			List<int> clone = new List<int>();
			clone.Add(counters[0]);
			bool result = true;
			for (byte i = 1; i < counters.Count && result; ++i)
			{
				result = !clone.Contains(counters[i]);
				if (result)
				{
					clone.Add(counters[i]);
				}
			}
			return result;
		}

		public bool AddExample(DiagrammPartInfo info)
		{
			Range = info.Range;
			bool result = Range == info.Range;
			if (result)
			{
				SearchQueue.Enqueue(info);
			}
			return result;
		}

		public bool Search()
		{
			InitCloneCandidates();
			Results.Clear();
			Processed.Clear();
			while (SearchQueue.Count > 0)
			{
				DiagrammPartInfo key = SearchQueue.Dequeue();
				List<DiagrammPartInfo> values = AllCloneCandidates.FindAll(key.IsLikeA);
				if (values.Count > 0)
				{
					Results.Add(key, values);
				}
				Processed.Add(key);
			}
			return Results.Count > 0;
		}
	}
}